#include "DXUT.h"
#include "StreamingSoundPlayer.h"
#include <dxerr.h>

DWORD WINAPI NotificationProcedure( LPVOID lpParameter );


CStreamingSoundPlayer::CStreamingSoundPlayer(CSoundManager*  pSoundManger,TCHAR* strFileName,bool bPlayingBackGround ,bool bLooped )
:m_pSoundManager(pSoundManger)
,m_pStreamingSound(NULL)
,m_hNotificationEvent(NULL)
,m_dwNotifyThreadID(0)
,m_hNotifyThread(NULL)
,m_bLooped(bLooped)
{
	m_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

	// Create a thread to handle DSound notifications
	m_hNotifyThread = CreateThread( NULL, 0, NotificationProcedure, 
		this, 0, &m_dwNotifyThreadID );

	OpenWave(strFileName,bPlayingBackGround);

}

CStreamingSoundPlayer::~CStreamingSoundPlayer(void)
{
	SAFE_DELETE( m_pStreamingSound );

	PostThreadMessage( m_dwNotifyThreadID, WM_QUIT, 0, 0 );
	WaitForSingleObject( m_hNotifyThread, INFINITE );

	CloseHandle( m_hNotificationEvent );
}




//-----------------------------------------------------------------------------
// Name: LoadWaveAndCreateBuffer()
// Desc: Loads the wave file, and create a DirectSound buffer.  Since we are
//       streaming data into the buffer, the buffer will be filled with data 
//       when the sound is played, and as notification events are signaled
//-----------------------------------------------------------------------------
bool CStreamingSoundPlayer::OpenWave( TCHAR* strFileName,bool bPlayingBackGround )
{
	HRESULT   hr;
	CWaveFile waveFile;
	DWORD     dwNotifySize;

	// Load the wave file
	if( FAILED( hr = waveFile.Open( strFileName, NULL, WAVEFILE_READ ) ) )
	{
		waveFile.Close();
		return false;
	}

	if( waveFile.GetSize() == 0 )
	{
		waveFile.Close();
		return false;
	}

	// The wave file is valid, and waveFile.m_pwfx is the wave's format
	// so we are done with the reader.
	waveFile.Close();

	// This samples works by dividing a 3 second streaming buffer into 
	// NUM_PLAY_NOTIFICATIONS (16) pieces.  It creates a notification for each
	// piece and when a notification arrives then it fills the circular streaming 
	// buffer with new wav data over the sound data which was just played

	// Determine the g_dwNotifySize.  It should be an integer multiple of nBlockAlign
	DWORD nBlockAlign = (DWORD)waveFile.m_pwfx->nBlockAlign;
	INT nSamplesPerSec = waveFile.m_pwfx->nSamplesPerSec;
	dwNotifySize = nSamplesPerSec * 3 * nBlockAlign / NUM_PLAY_NOTIFICATIONS;
	dwNotifySize -= dwNotifySize % nBlockAlign;   

	// Create a new sound
	SAFE_DELETE( m_pStreamingSound );

	DWORD dwFlag = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2;
	if (bPlayingBackGround)
	{
		dwFlag = dwFlag | DSBCAPS_GLOBALFOCUS;
	}
	// Set up the direct sound buffer.  Request the NOTIFY flag, so
	// that we are notified as the sound buffer plays.  Note, that using this flag
	// may limit the amount of hardware acceleration that can occur. 
	if( FAILED( hr = m_pSoundManager->CreateStreaming( &m_pStreamingSound, strFileName, 
		dwFlag , 
		GUID_NULL, NUM_PLAY_NOTIFICATIONS, 
		dwNotifySize, m_hNotificationEvent ) ) )
	{
		if( hr != DSERR_BADFORMAT && hr != E_INVALIDARG )
			DXTRACE_ERR( TEXT("CreateStreaming"), hr );

		return false;
	}

	return true;
}




//-----------------------------------------------------------------------------
// Name: PlayBuffer()
// Desc: Reset the buffer, fill it with sound, and starting it playing
//-----------------------------------------------------------------------------
HRESULT CStreamingSoundPlayer::Play( )
{
	HRESULT hr;

	if( NULL == m_pStreamingSound )
		return E_FAIL; // Sanity check

	if( FAILED( hr = m_pStreamingSound->Reset() ) )
		return DXTRACE_ERR( TEXT("Reset"), hr );

	// Fill the entire buffer with wave data, and if the wav file is small then
	// repeat the wav file if the user wants to loop the file, otherwise fill in
	// silence 
	LPDIRECTSOUNDBUFFER pDSB = m_pStreamingSound->GetBuffer( 0 );
	if( FAILED( hr = m_pStreamingSound->FillBufferWithSound( pDSB, m_bLooped ) ) )
		return DXTRACE_ERR( TEXT("FillBufferWithSound"), hr );

	// Always play with the LOOPING flag since the streaming buffer
	// wraps around before the entire WAV is played
	if( FAILED( hr = m_pStreamingSound->Play( 0, DSBPLAY_LOOPING ) ) )
		return DXTRACE_ERR( TEXT("Play"), hr );

	return S_OK;
}




void CStreamingSoundPlayer::Stop()
{
	if( m_pStreamingSound )
	{
		m_pStreamingSound->Stop();
		m_pStreamingSound->Reset();
	}
}

inline bool CStreamingSoundPlayer::IsPlaying()
{
	return m_pStreamingSound->IsSoundPlaying();
}
//-----------------------------------------------------------------------------
// Name: NotificationProc()
// Desc: Handles dsound notifcation events
//-----------------------------------------------------------------------------
DWORD WINAPI NotificationProcedure( LPVOID lpParameter )
{
	HRESULT hr;
	CStreamingSoundPlayer *    pPlayer = (CStreamingSoundPlayer *) lpParameter;
	MSG     msg;
	DWORD   dwResult;
	BOOL    bDone = FALSE;
	BOOL    bLooped;

	while( !bDone ) 
	{ 
		dwResult = MsgWaitForMultipleObjects( 1, &pPlayer->m_hNotificationEvent, 
			FALSE, INFINITE, QS_ALLEVENTS );
		switch( dwResult )
		{
		case WAIT_OBJECT_0 + 0:
			// g_hNotificationEvent is signaled

			// This means that DirectSound just finished playing 
			// a piece of the buffer, so we need to fill the circular 
			// buffer with new sound from the wav file
			bLooped = pPlayer->m_bLooped;//( IsDlgButtonChecked( hDlg, IDC_LOOP_CHECK ) == BST_CHECKED );
			if( FAILED( hr = pPlayer->m_pStreamingSound->HandleWaveStreamNotification( bLooped ) ) )
			{
				DXTRACE_ERR( TEXT("HandleWaveStreamNotification"), hr );
				bDone = TRUE;
			}

			break;

		case WAIT_OBJECT_0 + 1:
			// Messages are available
			while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
			{ 
				if( msg.message == WM_QUIT )
					bDone = TRUE;
			}
			break;
		}
	}

	return 0;
}











